package com.atguigu.search.utils;

import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.HttpAsyncResponseConsumerFactory;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class  ElasticsearchUtil {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 创建索引
     *
     * @param index 索引
     * @return
     */
    public boolean createIndex(String index) throws IOException {
        if (isIndexExist(index)) {
            return false;
        }
        //1.创建索引请求
        CreateIndexRequest request = new CreateIndexRequest(index);

        //2.执行客户端请求
        CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        return response.isAcknowledged();
    }
    /**
     * 创建索引
     *
     * @param index   索引
     * @param setting 设置
     * @return boolean
     * @throws IOException ioexception
     */
    public boolean createIndex(String index, Settings setting) throws IOException {
        if (isIndexExist(index)) {
            return false;
        }
        //1.创建索引请求
        CreateIndexRequest request = new CreateIndexRequest(index,setting);
        //2.执行客户端请求
        CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        return response.isAcknowledged();
    }
    /**
     * 删除索引
     *
     * @param index
     * @return
     */
    public boolean deleteIndex(String index) throws IOException {
        if (!isIndexExist(index)) {
            return false;
        }
        //删除索引请求
        DeleteIndexRequest request = new DeleteIndexRequest(index);
        //执行客户端请求
        AcknowledgedResponse delete = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);


        return delete.isAcknowledged();
    }

    /**
     * 判断索引是否存在
     *
     * @param index
     * @return
     */
    public boolean isIndexExist(String index) throws IOException {

        GetIndexRequest request = new GetIndexRequest(index);

        boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);

        return exists;
    }

    /***
     * 批量插入
     * @param list
     * @param index
     * @throws IOException
     */

    public String bulkPutIndex(List<Map<String, Object>> list, String index) {
        BulkRequest request = new BulkRequest();
        for (int j = 0; j < list.size(); j++) {
            Map<String, Object> item = list.get(j);
            request.add(new IndexRequest(index).source(item));
        }
        try {
            BulkResponse bulk = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
            if (bulk.status().getStatus() == 200) {
                return "success";
            } else {

                return "error";
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 数据添加，正定ID
     *
     * @param jsonObject 要增加的数据
     * @param index      索引，类似数据库
     * @param id         数据ID, 为null时es随机生成
     * @return
     */
    public String addData(JSONObject jsonObject, String index, String id) throws IOException {

        //创建请求
        IndexRequest request = new IndexRequest(index);
        //规则 put /test_index/_doc/1
        request.id(id);
        request.timeout(TimeValue.timeValueSeconds(1));
        //将数据放入请求 json
        IndexRequest source = request.source(jsonObject, XContentType.JSON);


        //客户端发送请求
        IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
        return response.getId();
    }


    /**
     * 数据添加 随机id
     *
     * @param jsonObject 要增加的数据
     * @param index      索引，类似数据库
     * @return
     */
    public String addData(JSONObject jsonObject, String index) throws IOException {

        return addData(jsonObject, index, UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
    }

    /**
     * 通过ID删除数据
     *
     * @param index 索引，类似数据库
     * @param id    数据ID
     */
    public void deleteDataById(String index, String id) throws IOException {
        //删除请求
        DeleteRequest request = new DeleteRequest(index, id);
        //执行客户端请求
        DeleteResponse delete = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
    }

    /**
     * 通过查询删除
     *
     * @param index        索引
     * @param queryBuilder 查询构建器
     * @throws IOException ioexception
     */
    public void deleteByQuery(String index, QueryBuilder queryBuilder) throws IOException {
        DeleteByQueryRequest request = new DeleteByQueryRequest(index);
        request.setQuery(queryBuilder);
        //request.setBatchSize(1000);
        //执行客户端请求
        restHighLevelClient.deleteByQuery(request, RequestOptions.DEFAULT);
    }


    /***
     * 删除索引
     * @param index
     * @throws IOException
     */
    public void deleteByData(String index) throws IOException {
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index);
        AcknowledgedResponse deleteResponse = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
    }

    /**
     * 通过ID 更新数据
     *
     * @param object 要增加的数据
     * @param index  索引，类似数据库
     * @param id     数据ID
     * @return
     */
    public void updateDataById(JSONObject object, String index, String id) throws IOException {
        //更新请求
        UpdateRequest update = new UpdateRequest(index, id);

        //保证数据实时更新
        //update.setRefreshPolicy("wait_for");

        update.timeout("1s");
        update.doc(object, XContentType.JSON);
        //执行更新请求
        UpdateResponse update1 = restHighLevelClient.update(update, RequestOptions.DEFAULT);
    }


    /***
     * 查询es数据总数
     * @return
     * @throws IOException
     */
    public int selectAllCount(String index) throws IOException {
        int result = 0;
        if (isIndexExist(index) == false) {
            createIndex(index);
        }

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        CountRequest countRequest = new CountRequest();
        //构造查询条件
        // searchSourceBuilder.query(QueryBuilders.termQuery("fieldName", 1));
        countRequest.indices(index).source(searchSourceBuilder);
        CountResponse countResponse = null;
        try {
            countResponse = restHighLevelClient.count(countRequest, RequestOptions.DEFAULT);
            result = (int) countResponse.getCount();

        } catch (IOException e) {
        }
        return result;
    }


    /**
     * 通过ID 更新数据,保证实时性
     *
     * @param object 要增加的数据
     * @param index  索引，类似数据库
     * @param id     数据ID
     * @return
     */
    public void updateDataByIdNoRealTime(JSONObject object, String index, String id) throws IOException {
        //更新请求
        UpdateRequest update = new UpdateRequest(index, id);

        //保证数据实时更新
        update.setRefreshPolicy("wait_for");

        update.timeout("1s");
        update.doc(object, XContentType.JSON);
        //执行更新请求
        UpdateResponse update1 = restHighLevelClient.update(update, RequestOptions.DEFAULT);
    }


    /**
     * 通过ID获取数据
     *
     * @param index  索引，类似数据库
     * @param id     数据ID
     * @param fields 需要显示的字段，逗号分隔（缺省为全部字段）
     * @return
     */
    public Map<String, Object> searchDataById(String index, String id, String fields) throws IOException {
        GetRequest request = new GetRequest(index, id);
        if (StringUtils.isNotEmpty(fields)) {
            //只查询特定字段。如果需要查询所有字段则不设置该项。
            request.fetchSourceContext(new FetchSourceContext(true, fields.split(","), Strings.EMPTY_ARRAY));
        }
        GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
        Map<String, Object> map = response.getSource();
        if(map!=null){

            //为返回的数据添加id
            map.put("id", response.getId());
        }
        return map;
    }

    /**
     * 通过ID判断文档是否存在
     *
     * @param index 索引，类似数据库
     * @param id    数据ID
     * @return
     */
    public boolean existsById(String index, String id) throws IOException {
        GetRequest request = new GetRequest(index, id);
        //不获取返回的_source的上下文
        request.fetchSourceContext(new FetchSourceContext(false));
        request.storedFields("_none_");
        return restHighLevelClient.exists(request, RequestOptions.DEFAULT);
    }

    /**
     * 获取低水平客户端
     *
     * @return
     */
    public RestClient getLowLevelClient() {
        return restHighLevelClient.getLowLevelClient();
    }


    /**
     * 高亮结果集 特殊处理
     * map转对象 JSONObject.parseObject(JSONObject.toJSONString(map), Content.class)
     *
     * @param searchResponse
     * @param highlightField
     */
    public Map<String, Object> setSearchResponses(SearchResponse searchResponse, String highlightField, Integer total) {
        //解析结果
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit hit : searchResponse.getHits().getHits()) {
            Map<String, HighlightField> high = hit.getHighlightFields();
            HighlightField title = high.get(highlightField);

            hit.getSourceAsMap().put("id", hit.getId());

            Map<String, Object> sourceAsMap = hit.getSourceAsMap();//原来的结果
            //解析高亮字段,将原来的字段换为高亮字段
            if (title != null) {
                Text[] texts = title.fragments();
                String nTitle = "";
                for (Text text : texts) {
                    nTitle += text;
                }
                //替换
                sourceAsMap.put(highlightField, nTitle);
            }
            list.add(sourceAsMap);
        }
        Map<String, Object> map = new HashMap<>();
        map.put("data", list);
        map.put("total", total);
        return map;
    }


    /**
     * 高亮结果集 特殊处理
     * map转对象 JSONObject.parseObject(JSONObject.toJSONString(map), Content.class)
     *
     * @param searchResponse
     * @param highlightField
     */
    public Map<String, Object> setSearchResponse(SearchResponse searchResponse, String highlightField, Integer total) {
        //解析结果
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit hit : searchResponse.getHits().getHits()) {
            Map<String, Object> result = new HashMap<>();
            Map<String, HighlightField> high = hit.getHighlightFields();
            Integer indexs = highlightField.indexOf(",");
            if (indexs > 0) {
                for (String str : highlightField.split(",")) {
                    HighlightField title = high.get(str);

                    hit.getSourceAsMap().put("id", hit.getId());

                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();//原来的结果
                    //解析高亮字段,将原来的字段换为高亮字段
                    if (title != null) {
                        Text[] texts = title.fragments();
                        String nTitle = "";
                        for (Text text : texts) {
                            nTitle += text;
                        }
                        //替换
                        sourceAsMap.put(str, nTitle);


                    }
                    result = sourceAsMap;
                }
                list.add(result);
            } else {
                HighlightField title = high.get(highlightField);

                hit.getSourceAsMap().put("id", hit.getId());

                Map<String, Object> sourceAsMap = hit.getSourceAsMap();//原来的结果
                //解析高亮字段,将原来的字段换为高亮字段
                if (title != null) {
                    Text[] texts = title.fragments();
                    String nTitle = "";
                    for (Text text : texts) {
                        nTitle += text;
                    }
                    //替换
                    sourceAsMap.put(highlightField, nTitle);

                }

                list.add(sourceAsMap);
            }


        }
        Map<String, Object> map = new HashMap<>();
        map.put("data", list);
        map.put("total", total);
        return map;
    }

    public Map<String, Object> setSearchResponse(SearchResponse searchResponse, Integer total) {
        Map<String, Object> map = new HashMap<>();
        List<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit hit : searchResponse.getHits().getHits()) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            sourceAsMap.put("id",hit.getId());
            list.add(sourceAsMap);
        }
        map.put("data",list);
        map.put("total", total);
        return map;
    }

    /**
     * 查询并分页
     *
     * @param index          索引名称
     * @param query          查询条件
     * @param size           文档大小限制
     * @param from           从第几页开始
     * @param fields         需要显示的字段，逗号分隔（缺省为全部字段）
     * @param sortField      排序字段
     * @param highlightField 高亮字段
     * @return
     */
    public Map<String, Object> searchListData(String index,
                                              SearchSourceBuilder query,
                                              Integer size,
                                              Integer from,
                                              String fields,
                                              String sortField,
                                              String highlightField) throws IOException {
        SearchRequest request = new SearchRequest(index);
        SearchSourceBuilder builder = query;
        if (StringUtils.isNotEmpty(fields)) {
            //只查询特定字段。如果需要查询所有字段则不设置该项。
            builder.fetchSource(new FetchSourceContext(true, fields.split(","), Strings.EMPTY_ARRAY));
        }
        from = from <= 0 ? 0 : from * size;
        //设置确定结果要从哪个索引开始搜索的from选项，默认为0
        builder.from(from);
        builder.size(size);
        if (StringUtils.isNotEmpty(sortField)) {
            //排序字段，注意如果proposal_no是text类型会默认带有keyword性质，需要拼接.keyword
            builder.sort(sortField, SortOrder.DESC);
        }
        if (ObjectUtil.isNotNull(highlightField)){
            Integer indexs = highlightField.indexOf(",");
            HighlightBuilder highlight = new HighlightBuilder();
            if (indexs > 0) {
                for (String str : highlightField.split(",")) {
                    highlight.field(str);
                }

            } else {
                highlight.field(highlightField);


            }

            highlight.preTags("<font color=\"red\">");
            highlight.postTags("</font>");
            highlight.highlighterType("unified");
            highlight.requireFieldMatch(true);//多次段高亮需要设置为false
            //下面这两项,如果你要高亮如文字内容等有很多字的字段,必须配置,不然会导致高亮不全,文章内容缺失等
            highlight.fragmentSize(800000); //最大高亮分片数
            highlight.numOfFragments(0); //从第一个分片获取高亮片段
            //不返回源数据。只有条数之类的数据。
            //builder.fetchSource(false);
            builder.highlighter(highlight);
        }

        request.source(builder);
        //SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

        RequestOptions.Builder option = RequestOptions.DEFAULT.toBuilder();
        option.setHttpAsyncResponseConsumerFactory(new HttpAsyncResponseConsumerFactory.HeapBufferedResponseConsumerFactory(500 * 1024 * 1024));
        SearchResponse response = restHighLevelClient.search(request, option.build());

        if (response.status().getStatus() == 200) {
            // 解析对象
            if (ObjectUtil.isNotNull(highlightField)){
                return setSearchResponse(response, highlightField, (int) response.getHits().getTotalHits().value);
            }
            return setSearchResponse(response, (int) response.getHits().getTotalHits().value);
        }
        return null;
    }


    /***
     *
     * @param index 索引
     * @param keyword 关键字
     * @param pageNo
     * @param pageSize
     * @return
     * @throws IOException
     */
    public List<Map<String, Object>> searchPage(String index, String keyword, int pageNo, int pageSize) throws IOException {
//        指定要查询的索引
        SearchRequest request = new SearchRequest(index);

//        设置分页条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        pageNo = pageNo == 0 ? 0 : pageNo;
        pageSize = pageSize == 0 ? 10 : pageSize;
        searchSourceBuilder.from(pageNo);
        searchSourceBuilder.size(pageSize);

//        输入的关键字匹配的字段
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("opeInterface.keyword", keyword);
        searchSourceBuilder.query(termQueryBuilder);


//        执行
        request.source(searchSourceBuilder);
        SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        search.getHits().forEach(hit -> {
            Map<String, Object> map = hit.getSourceAsMap();
            list.add(map);

        });

        return list;

    }


}
